package com.xu.sso.core.utils;

import com.alibaba.fastjson.JSONObject;
import com.xu.sso.core.config.SystemConfig;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.ScriptOutputType;
import io.lettuce.core.SetArgs;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RedisUtil implements DisposableBean {

    private final Logger log = LoggerFactory.getLogger(RedisUtil.class);

    @Autowired
    private SystemConfig config;
    private RedisClient redisClient = null;
    private StatefulRedisConnection<String, String> connect = null;

    private RedisClient getInstance() {
        if (null == redisClient) {
            synchronized (RedisUtil.class) {
                if (null == redisClient) {
                    RedisURI redisURI = null;
                    if (config.getHost().contains(",")) {
                        RedisURI.Builder builder = RedisURI.builder().withDatabase(config.getDatabase());
                        if (null != config.getPassword() && config.getPassword().trim().length() > 0) {
                            builder.withPassword(config.getPassword());
                        }
                        String[] hosts = config.getHost().split(",");
                        for (String host : hosts) {
                            String[] uri = host.split(":");
                            builder.withSentinel(uri[0], Integer.valueOf(uri[1]));
                        }
                        redisURI = builder.withSentinelMasterId(config.getMasterId()).build();
                    } else {
                        String uri = "redis://";
                        if (null != config.getPassword() && config.getPassword().trim().length() > 0) {
                            uri += config.getPassword() + "@";
                        }
                        uri += config.getHost() + "/" + config.getDatabase();
                        redisURI = RedisURI.create(uri);
                    }
                    redisClient = RedisClient.create(redisURI);
                    log.info(">>>>>>>>>>>>>>>>>>>>>>redis init success...");
                }
            }
        }
        return redisClient;
    }

    public StatefulRedisConnection<String, String> getConnect() {
        if (null == connect || !connect.isOpen()) {
            synchronized (RedisUtil.class) {
                if (null == connect || !connect.isOpen()) {
                    connect = getInstance().connect();
                }
            }
        }
        return connect;
    }

    public void close(StatefulRedisConnection<String, String> connect) {
        if (null != connect && connect.isOpen()) {
            connect.close();
        }
    }

    public boolean set(String key, String value) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value);
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean set(String key, String value, long ex) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.setex(key, ex, value);
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, String value) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value, SetArgs.Builder.nx());
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, String value, long ex) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value, SetArgs.Builder.nx().ex(ex));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean set(String key, Object obj) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean set(String key, Object obj, long ex) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.setex(key, ex, JSONObject.toJSONString(obj));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, Object obj) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj), SetArgs.Builder.nx());
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, Object obj, long ex) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj), SetArgs.Builder.nx().ex(ex));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public String getStringValue(String key) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            return commands.get(key);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis getStringValue error === {}", e);
        }
        return null;
    }

    public <T> T getObjectValue(String key, Class<T> clazz) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            String result = commands.get(key);
            return JSONObject.parseObject(result, clazz);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis getObjectValue error === {}", e);
        }
        return null;
    }

    public Long getUid(String key) {
        return getInstance().connect().sync().incr(key);
    }

    public <T> T eval(String lua, String[] keys, String[] vals, ScriptOutputType type) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            return commands.eval(lua, type, keys, vals);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis eval error === {}", e);
        }
        return null;
    }

    public boolean del(String key) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            long result = commands.del(key);
            log.info(">>>>>>>>>>del result==={}", result);
            if (result > 0) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis del error === {}", e);
        }
        return false;
    }

    /**
     * 判断key是否存在
     *
     * @param key
     * @return
     */
    public boolean exists(String key) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            long result = commands.exists(key);
            if (result > 0) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis exists error === {}", e);
        }
        return false;
    }

    /**
     * 重置存活时间
     *
     * @param key
     * @param seconds
     * @return
     */
    public boolean expire(String key, int seconds) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            return commands.expire(key, seconds);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis expire error === {}", e);
        }
        return false;
    }

    /**
     * 设置存活到期时间
     *
     * @param key
     * @param unixTime 到期时间的时间戳
     * @return
     */
    public boolean expireAt(String key, long unixTime) {
        try {
            RedisCommands<String, String> commands = getConnect().sync();
            return commands.expireat(key, unixTime);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis expireAt error === {}", e);
        }
        return false;
    }

    @Override
    public void destroy() throws Exception {
        close(connect);
    }

}
